<?php
/**
 * 核心函数
 */

function jfilter($val, $filter)
{
	$filter = strtolower($filter);
	switch ($filter) {
	case 'int': $val = (int) $val; break;
	case 'float': $val = (float) $val; break;
	case 'bool': case 'boolean': $val = (bool) $val; break;
	case 'num': case 'number': $val = (is_numeric($val) ? $val : 0); break;
	case 'trim': $val = trim($val); break;
	case 'txt': $val = trim(htmlspecialchars(strip_tags($val))); break;
	case 'html': $val = htmlspecialchars($val); break;
	case 'url': $val = (($val && preg_match("/^(https?\:\/\/|www\.)([A-Za-z0-9_\-]+\.)+[A-Za-z]{2,4}(\/[\w\d\/=\?%\-\&_~`@\[\]\:\+\#]*([^<>\'\"\n])*)?$/", $val)) ? $val : false); break;
	case 'email': $val = (($val && false !== strpos($val, '@') && preg_match('~^[-_.[:alnum:]]+@((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\.)+([a-z]{2,4})|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$~i', $val)) ? $val : false); break;
	case 'zip': $val = (($val && is_numeric($val) && preg_match('~^\d{6}$~', $val)) ? $val : false); break;
	case 'qq': $val = (($val && is_numeric($val) && preg_match('~^[1-9]\d{4,10}$~', $val)) ? $val : false); break;
	case 'mobile': $val = (($val && is_numeric($val) && preg_match('~^((?:13|15|18|17)\d{9}|0(?:10|2\d|[3-9]\d{2})[1-9]\d{6,7})$~', $val)) ? $val : false); break;
	case 'chinese': $val = (($val && preg_match('~^(?:[\x7f-\xff][\x7f-\xff])+$~', $val)) ? $val : false); break;
	case 'english': $val = (($val && preg_match('~^[A-Za-z]+$~', $val)) ? $val : false); break;
	case 'username': $val = (($val && !is_numeric($val) && preg_match('~^[\w\d_]{1, 30}$~', $val)) ? $val : false); break;
	default: break;
	}
	return $val;
}

function get_param($key, $method = 'PG', $filter = '')
{
	$method = strtoupper($method);
	switch ($method) {
	case 'POST':
	case 'P':
		$var = &$_POST;
		break;
	case 'GET':
	case 'G':
		$var = &$_GET;
		break;
	case 'COOKIE':
	case 'C':
		$var = &$_COOKIE;
		break;
	default:
		if (isset($_POST[$key])) {
			$var = &$_POST;
		} else {
			$var = &$_GET;
		}
		break;
	}
	$val = (isset($var[$key]) ? $var[$key] : null);

	if ($filter) {
		$val = jfilter($val, $filter);
	}
	return $val;
}

function jsetcookie($var, $value, $life = 0, $httponly = false)
{
	$var = $GLOBALS['config']['cookie_prefix'] . $var;
	$_COOKIE[$var] = $value;

	if ('' == $value || $life < 0) {
		$value = '';
		$life = -1;
	}

	$life = ($life > 0 ? ($GLOBALS['timestamp'] + $life) : ($life < 0 ? ($GLOBALS['timestamp'] - 86400000) : 0));
	$path = (($httponly && PHP_VERSION < '5.2.0') ? $GLOBALS['config']['cookie_path'] . '; HttpOnly' : $GLOBALS['config']['cookie_path']);
	$domain = ($GLOBALS['config']['cookie_domain'] ? $GLOBALS['config']['cookie_domain'] : '');
	$secure = ($_SERVER['SERVER_PORT'] == 443 ? 1 : 0);

	if (PHP_VERSION < '5.2.0') {
		setcookie($var, $value, $life, $path, $domain, $secure);
	} else {
		setcookie($var, $value, $life, $path, $domain, $secure, $httponly);
	}
}

function jgetcookie($var)
{
	$var = $GLOBALS['config']['cookie_prefix'] . $var;
	return $_COOKIE[$var];
}

function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
{
	$ckey_length = 4;
	if (! $key) {
		$key = $GLOBALS['config']['auth_key'];
	}
	$key = md5($key);
	$keya = md5(substr($key, 0, 16));
	$keyb = md5(substr($key, 16, 16));
	$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), - $ckey_length)) : '';

	$cryptkey = $keya . md5($keya . $keyc);
	$key_length = strlen($cryptkey);

	$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
	$string_length = strlen($string);

	$result = '';
	$box = range(0, 255);

	$rndkey = array();
	for($i = 0; $i <= 255; $i++) {
		$rndkey[$i] = ord($cryptkey[$i % $key_length]);
	}

	for($j = $i = 0; $i < 256; $i++) {
		$j = ($j + $box[$i] + $rndkey[$i]) % 256;
		$tmp = $box[$i];
		$box[$i] = $box[$j];
		$box[$j] = $tmp;
	}

	for($a = $j = $i = 0; $i < $string_length; $i++) {
		$a = ($a + 1) % 256;
		$j = ($j + $box[$a]) % 256;
		$tmp = $box[$a];
		$box[$a] = $box[$j];
		$box[$j] = $tmp;
		$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
	}

	if ($operation == 'DECODE') {
		if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
			return substr($result, 26);
		} else {
			return '';
		}
	} else {
		return $keyc . str_replace('=', '', base64_encode($result));
	}
}

function random($length)
{
	$hash = '';
	$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
	$max = strlen($chars) - 1;
	for($i = 0; $i < $length; $i++) {
		$hash .= $chars[rand(0, $max)];
	}
	return $hash;
}

/**
 * 获取当前客户端的 IP。
 */
function client_ip()
{
	$ipd = '127.0.0.1';
	$vs = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR');
	foreach ($vs as $v) {
		$ip = (getenv($v) ? getenv($v) : $_SERVER[$v]);
		if ($ip && strcasecmp($ip, 'unknown') && preg_match('~^([0-9]{1,3}\.){3}[0-9]{1,3}$~', $ip)) {
			break;
		} else {
			$ip = $ipd;
		}
	}
	$ips = explode('.', $ip);
	for ($i = 0; $i < 4; $i++) {
		$ipi = (is_numeric($ips[$i]) ? (int)$ips[$i] : - 1);
		if ($ipi < 0 || $ipi > 255) {
			$ip = $ipd;
			break;
		}
	}
	return $ip;
}

/**
 * 计算当前场景下的自动跳转链接，并埋入 cookie 中，用于完成当前操作后的回跳。
 * 优先顺序为：$jump → $_GET['ref'] → $_COOKIES['jump'] → $_SERVER['HTTP_REFERER']
 * @param string $jump
 * @return string
 */
function saveAutoJump($jump = null)
{
	if (!isset($jump)) {
		$jump = get_param('ref', 'G');
	}
	if (!isset($jump)) {
		$jump = jgetcookie('jump');
	}
	if (!isset($jump)) {
		$jump = $_SERVER['HTTP_REFERER'];
		if (strpos($jump, 'login') !== false ||
			strpos($jump, 'logout') !== false ||
			strpos($jump, 'register') !== false ||
			strpos($jump, 'forget') !== false ||
			strpos($jump, 'trust') !== false) {
			unset($jump);
		}
	}
	if (isset($jump)) {
		jsetcookie('jump', $jump);
	}
	return $jump;
}

/**
 * 获取自动跳转链接，或者直接重定向跳过去。
 * 优先顺序为：$jump → $_COOKIES['jump'] → '/'
 * @param string $jump
 * @param string $go
 * @return string
 */
function loadAutoJump($jump = null, $go = false)
{
	if (!isset($jump)) {
		$jump = jgetcookie('jump');
	}
	if (!isset($jump)) {
		$jump = '/';
	}
	jsetcookie('jump', '', - 311040000);
	if ($go) {
		header('Location: ' . $jump);
		exit();
	}
	return $jump;
}

/**
 * 检测一个字符串是否以给定的字符串开头
 * @param string $str 待检测的字符串
 * @param string $prefix 预期的前缀
 * @param boolean $ignoreCase true 表示忽略大小写
 */
function strBeginsWith($str, $prefix, $ignoreCase = false)
{
	if ($ignoreCase) {
		return strtolower(substr($str, 0, strlen($prefix))) === strtolower($prefix);
	}
	return substr($str, 0, strlen($prefix)) === $prefix;
}

/**
 * 检测一个字符串是否以给定的字符串结尾
 * @param string $str 待检测的字符串
 * @param string $suffix 预期的尾缀
 * @param boolean $ignoreCase true 表示忽略大小写
 */
function strEndsWith($str, $suffix, $ignoreCase = false)
{
	if ($ignoreCase) {
		return strtolower(substr($str, - strlen($suffix))) === strtolower($suffix);
	}
	return substr($str, - strlen($suffix)) === $suffix;
}

/**
 * 从指定的 array 中提取出特定的一组 key/value。
 * @param array $source 源 array。
 * @param array $ref 指定所需的 key 及其缺省值。
 */
function extractParams($source, $ref)
{
	$params = array();
	foreach ($ref as $key => $def) {
		if (array_key_exists($key, $source)) {
			$params[$key] = $source[$key];
		} else {
			$params[$key] = $def;
		}
	}
	return $params;
}

/**
 * 按照 keys 指定的属性列表，把 $src 中的元素复制到 $dest 中去。
 * 如果 $src 中不存在指定的元素，则用 $def 值代替。
 * 如果 $src 中的值（以及来自 $def 的值）为 null，则不覆盖。
 * 支持 . 形式的属性级联，复制后目标属性名就是用 _ 替换 . 之后的字符串。
 * @param array $dest
 * @param array $src
 * @param array $keys
 * @param string $def
 */
function copyArrayElements(&$dest, $src, $keys, $def = null)
{
	foreach ($keys as $key) {
		$key1 = str_replace('.', '_', $key);
		$value = $src;
		foreach (explode('.', $key) as $segkey) {
			if (is_array($value) && array_key_exists($segkey, $value)) {
				$value = $value[$segkey];
			} else {
				$value = $def;
				break;
			}
		}
		if (isset($value)) {
			$dest[$key1] = $value;
		}
	}
}

/**
 * 确保输入的字符串中只含有数字。
 * 用于防范“SQL 注入”。
 */
function safeIntString($str)
{
	$str = preg_replace('/[^\\d]/', '', $str);
	if (empty($str)) $str = '0';
	return $str;
}

/**
 * 确保输入的字符串中只含有字母、数字、下划线。
 * 用于防范“SQL 注入”。
 */
function safeSimpleName($str)
{
	return preg_replace('/[^\\w]/', '', $str);
}

/**
 * 确保输入的字符串中只含有字母、数字、下划线、空格、汉字。
 * 用于防范“SQL 注入”。
 */
function safeSimpleString($str)
{
	return preg_replace('/[^\\w \\x{80}-\\x{ff}]/', '', $str);
}

/**
 * 获取可安全读取的文件路径。确保返回的路径为指定根目录下游的内容。
 * @param string $root 指定的根目录。
 * @param string $abspath 指定的全路径（可能不是规范化的）。
 * @return string 安全的规范化路径，否则返回 false。
 */
function safeFilePath($root, $abspath)
{
	$root = realpath($root);
	$abspath = realpath($abspath);
	if ($abspath === false || substr($abspath, 0, strlen($root)) !== $root) {
		return false;
	}
	return $abspath;
}

/**
 * 获取所有文件中最后一个修改时间。
 * @param string $root 指定的根目录。
 * @param array $files 所有文件的相对路径。
 * @return number
 */
function findLastModifiedTime($root, $files)
{
	$last = 0;
	foreach ($files as $filename) {
		$filename = safeFilePath($root, $root . $filename);
		if (!empty($filename)) {
			$mtime = filemtime($filename);
			if ($mtime !== false && $last < $mtime) {
				$last = $mtime;
			}
		}
	}
	return $last;
}

/**
 * 检测当前运行环境是否为测试环境。
 */
function isTestStage()
{
	if (isset($_SERVER['HTTP_HOST'])) {
		return strBeginsWith($_SERVER['HTTP_HOST'], 'test.', true);
	}
	return gethostname() == 'hornet';
}

/**
 * 检测当前运行环境是否为调试环境。
 * Windows 环境即被判定为调试环境。
 */
function isDebugStage()
{
	return strBeginsWith(PHP_OS, 'WIN', true);
}

/**
 * 检测当前的运行环境。
 * @return string prod / test / debug
 */
function detectStage()
{
	if (isDebugStage()) return 'debug';
	if (isTestStage()) return 'test';
	return 'prod';
}
